"""
@author: 景云鹏
@email: 310491287@qq.com
@date: 2022/4/28
"""

from abc import ABCMeta, abstractmethod

from matplotlib import pyplot as plt
from numpy import *


class Algorithm(metaclass=ABCMeta):
    def __init__(self, f, size=5, noi=20):
        self.f = f
        self.size = size
        self.noi = noi
        self.helper = None

    @abstractmethod
    def _init(self):
        raise NotImplementedError

    @abstractmethod
    def _update(self):
        raise NotImplementedError

    @abstractmethod
    def get_result(self):
        raise NotImplementedError

    def get_point(self):
        raise NotImplementedError

    def run(self):
        self._init()
        for _ in range(self.noi - 1):
            self._update()
        return self.get_result()

    def run_and_figure(self):
        points = []
        self._init()
        points.append(self.get_point())
        check = self.noi // 20
        for i in range(1, self.noi):
            self._update()
            if i % check == 0:
                points.append(self.get_point())

        self.f.figure(points=points)

    def _get_bests(self):
        bests = []
        self._init()
        best, _ = self.get_result()
        bests.append(best)
        for _ in range(self.noi - 1):
            self._update()
            best, _ = self.get_result()
            bests.append(best)
        return bests

    def l_shaped(self, rerun=30):

        x = arange(self.size, self.size * (self.noi + 1), self.size)
        for i in range(rerun):
            bests = self._get_bests()
            plt.plot(x, bests)

        plt.plot(x, ones(len(x)) * self.f.min_value, c='k')
        plt.title(self.f.name)
        plt.show()

    def multirun(self, check_points=5, rerun=30):
        result_matrix = zeros(check_points * rerun).reshape(
            (rerun, check_points)
        )
        for i in range(rerun):
            self._init()
            check = self.noi // check_points
            c = 0
            for j in range(self.noi):
                self._update()
                if (j + 1) % check == 0:
                    result_matrix[i][c] = self.get_result()[0]
                    c += 1
        return result_matrix.mean(axis=0)
